package com.tsd.core.utils;

import com.tsd.core.constants.Constants;
import org.apache.http.HttpEntity;
import org.apache.http.HttpResponse;
import org.apache.http.HttpStatus;
import org.apache.http.NameValuePair;
import org.apache.http.client.HttpClient;
import org.apache.http.client.config.RequestConfig;
import org.apache.http.client.entity.UrlEncodedFormEntity;
import org.apache.http.client.methods.CloseableHttpResponse;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.config.Registry;
import org.apache.http.config.RegistryBuilder;
import org.apache.http.conn.socket.ConnectionSocketFactory;
import org.apache.http.conn.socket.PlainConnectionSocketFactory;
import org.apache.http.conn.ssl.SSLConnectionSocketFactory;
import org.apache.http.entity.StringEntity;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.HttpClientBuilder;
import org.apache.http.impl.client.HttpClients;
import org.apache.http.impl.conn.PoolingHttpClientConnectionManager;
import org.apache.http.message.BasicNameValuePair;
import org.apache.http.util.EntityUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import javax.net.ssl.SSLContext;
import javax.net.ssl.TrustManager;
import javax.net.ssl.X509TrustManager;
import javax.servlet.http.HttpServletRequest;
import java.io.IOException;
import java.net.InetAddress;
import java.net.UnknownHostException;
import java.nio.charset.StandardCharsets;
import java.security.KeyManagementException;
import java.security.NoSuchAlgorithmException;
import java.security.cert.CertificateException;
import java.security.cert.X509Certificate;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Set;

/**
 * web访问工具类
 *
 * @author Hillpool
 */
public class WebUtils {

    private static final Logger LOGGER = LoggerFactory.getLogger(WebUtils.class);
    private static final String SCHEME_HTTP = "http";
    private static final String SCHEME_HTTPS = "https";
    protected String token = null;
    private final HttpClient httpClient = HttpClientBuilder.create().build();

    public static WebUtils getInstance() {
        return new WebUtils();
    }

    /**
     * 获取HttpServletRequest的服务器IP
     *
     * @param request
     * @return
     */
    public static String getServerIp(HttpServletRequest request) {
        String ip = null;
        try {
            ip = InetAddress.getLocalHost().getHostAddress();
        } catch (UnknownHostException e) {
            e.printStackTrace();
        }
        return ip;
    }

    /**
     * 获取HttpServletRequest的服务器端口
     *
     * @param request
     * @return
     */
    public static int getServerPort(HttpServletRequest request) {
        int port = request.getServerPort();
        return port;
    }

    /**
     * 调用 API
     *
     * @param parameters
     * @return
     * @throws IOException
     */
    public String post(HttpPost method, String parameters) throws IOException {
        String body = null;
        HttpResponse response = null;
        if (method != null) {
            method.addHeader("Content-type", "application/json; charset=utf-8");
            method.setHeader("Accept", "application/json");
            method.setHeader(Constants.REQ_HEADER_NAME_TOKEN, token);
            if (parameters != null
                    && !"".equals(parameters.trim())) {
                method.setEntity(new StringEntity(parameters, StandardCharsets.UTF_8));
            }
            long startTime = System.currentTimeMillis();

            response = httpClient.execute(method);

            long endTime = System.currentTimeMillis();
            int statusCode = response.getStatusLine().getStatusCode();

            LOGGER.debug("statusCode:" + statusCode);
            LOGGER.debug("调用API 花费时间(单位：毫秒)：" + (endTime - startTime));
            if (statusCode != HttpStatus.SC_OK) {
                LOGGER.debug("Method failed:" + response.getStatusLine());
            }
            HttpEntity entity = response.getEntity();
            body = EntityUtils.toString(entity);
            EntityUtils.consume(entity);
        }
        return body;
    }

    public String postFormUrl(String url, Map<String, String> params) throws Exception {
        return postFormUrl(url, params, "UTF-8");
    }

    /**
     * Http post提交数据，编码为指定的格式
     *
     * @param url      提交地址
     * @param params   参数
     * @param encoding 参数编码
     * @return
     * @throws Exception
     */
    public String postFormUrl(String url, Map<String, String> params, String encoding) throws Exception {
        CloseableHttpResponse response = null;
        try {
            // 参数
            List<NameValuePair> param = new ArrayList<>();
            if (params != null) {
                // 添加参数
                for (Entry<String, String> entry : params.entrySet()) {
                    param.add(new BasicNameValuePair(entry.getKey(), entry.getValue()));
                }
            }
            CloseableHttpClient builder = HttpClientBuilder.create().build();
            HttpPost request = new HttpPost(url);
            HttpEntity entity = new UrlEncodedFormEntity(param, encoding);
            request.setEntity(entity);
            RequestConfig config = RequestConfig.custom().setConnectTimeout(20000).setSocketTimeout(60000).build();
            request.setConfig(config);
            response = builder.execute(request);
            if (response.getStatusLine().getStatusCode() == HttpStatus.SC_OK) {
                return EntityUtils.toString(response.getEntity(), encoding);
            } else {
                throw new Exception(EntityUtils.toString(response.getEntity(), encoding));
            }
        } finally {
            try {
                if (response != null) {
                    response.close();
                }
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
    }

    /**
     * Http post提交JSON数据，编码为指定的格式
     *
     * @param url  提交地址
     * @param json JSON参数
     * @return
     * @throws Exception
     */
    public String postJsonUrl(String url, String json) throws Exception {
        return postJsonUrl(url, json, null, "UTF-8");
    }

    /**
     * Http post提交JSON数据，编码为指定的格式
     *
     * @param url     提交地址
     * @param json    JSON参数
     * @param headers header
     * @return
     * @throws Exception
     */
    public String postJsonUrl(String url, String json, Map<String, Object> headers) throws Exception {
        return postJsonUrl(url, json, headers, "UTF-8");
    }

    /**
     * Http post提交JSON数据，编码为指定的格式
     *
     * @param url      提交地址
     * @param json     JSON参数
     * @param headers  header
     * @param encoding 参数编码
     * @return
     * @throws Exception
     */
    public String postJsonUrl(String url, String json, Map<String, Object> headers, String encoding) throws Exception {
        CloseableHttpResponse response = null;
        try {
            CloseableHttpClient builder = null;
            if (url.contains(SCHEME_HTTPS)) {
                builder = createSSLClientDefault();
            } else {
                builder = HttpClients.createDefault();
            }
            HttpPost request = new HttpPost(url);
            request.addHeader("Content-type", "application/json;charset=utf-8");
            request.setHeader("Accept", "application/json");
            if (headers != null) {
                Set<String> keys = headers.keySet();
                for (String key : keys) {
                    request.addHeader(key, headers.get(key).toString());
                }
            }
            if (!HlpUtils.isEmpty(json)) {
                StringEntity entity = new StringEntity(json, encoding);
                entity.setContentEncoding(encoding);
                entity.setContentType("application/json");
                request.setEntity(entity);
            }
            RequestConfig config = RequestConfig.custom().setConnectTimeout(20000).setSocketTimeout(60000).build();
            request.setConfig(config);
            response = builder.execute(request);
            if (response.getStatusLine().getStatusCode() == HttpStatus.SC_OK) {
                return EntityUtils.toString(response.getEntity(), encoding);
            } else {
                throw new Exception(EntityUtils.toString(response.getEntity(), encoding));
            }
        } finally {
            try {
                if (response != null) {
                    response.close();
                }
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
    }

    public String getFromURL(String url) {
        return getFromURL(url, "UTF-8");
    }

    public String getFromURL(String url, String encoding) {
        CloseableHttpResponse response = null;
        try {
            CloseableHttpClient builder = null;
            if (url.startsWith(SCHEME_HTTPS)) {
                builder = createSSLClientDefault();
            } else {
                builder = HttpClients.createDefault();
            }
            HttpGet request = new HttpGet(url);
            request.addHeader("Content-type", "application/json;charset=utf-8");
            request.setHeader("Accept", "application/json");
            RequestConfig config = RequestConfig.custom().setConnectTimeout(20000).setSocketTimeout(60000).build();
            request.setConfig(config);
            response = builder.execute(request);
            if (response.getStatusLine().getStatusCode() == HttpStatus.SC_OK) {
                return EntityUtils.toString(response.getEntity(), encoding);
            } else {
                throw new Exception(EntityUtils.toString(response.getEntity(), encoding));
            }
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            try {
                if (response != null) {
                    response.close();
                }
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
        return null;
    }

    private SSLContext createIgnoreVerifySSL() throws NoSuchAlgorithmException, KeyManagementException {
        SSLContext sc = SSLContext.getInstance("SSLv3");
        // 实现一个X509TrustManager接口，用于绕过验证，不用修改里面的方法
        X509TrustManager trustManager = new X509TrustManager() {
            @Override
            public void checkClientTrusted(X509Certificate[] paramArrayOfX509Certificate,
                                           String paramString) throws CertificateException {
            }

            @Override
            public void checkServerTrusted(X509Certificate[] paramArrayOfX509Certificate,
                                           String paramString) throws CertificateException {
            }

            @Override
            public X509Certificate[] getAcceptedIssuers() {
                return null;
            }
        };
        sc.init(null, new TrustManager[]{trustManager}, null);
        return sc;
    }

    private CloseableHttpClient createSSLClientDefault() {
        try {
            //采用绕过验证的方式处理https请求
            SSLContext sslcontext = createIgnoreVerifySSL();
            //设置协议http和https对应的处理socket链接工厂的对象
            Registry<ConnectionSocketFactory> socketFactoryRegistry = RegistryBuilder.<ConnectionSocketFactory>create()
                    .register("http", PlainConnectionSocketFactory.INSTANCE)
                    .register("https", new SSLConnectionSocketFactory(sslcontext))
                    .build();
            PoolingHttpClientConnectionManager connManager = new PoolingHttpClientConnectionManager(socketFactoryRegistry);
            return HttpClients.custom().setConnectionManager(connManager).build();
        } catch (Exception ex) {
            LOGGER.error(ex.getMessage());
        }
        return HttpClients.createDefault();
    }
}
